/**
 * @particulars 生命周期钩子函数
 */

import Watcher from "./observe/watcher";
import { createElementVNode, createTextVNode } from "./vnode";
import { patch } from "./vnode/patch";

// 1、挂载生命周期方法
export function initLifeCycle(Vue) {
  // _c('div', {style: {"border":" 1px solid"}}, _v(_s(name)+"hello"))
  // 处理节点属性
  Vue.prototype._c = function () {
    return createElementVNode(this, ...arguments);
  }

  // 处理文本内容
  Vue.prototype._v = function () {
    return createTextVNode(this, ...arguments);
  }

  // 处理插值语法的变量
  Vue.prototype._s = function (value) {
    if (typeof value !== 'object') return value;
    return JSON.stringify(value);
  }

  // 1-1、生成虚拟节点，这个render 就是模板编译之后的render 函数，=> _c('div', null, _v(_s(name) + 'hello'))
  Vue.prototype._render = function () {
    // 指定this 的指向，保证with 创建的作用域里的this 指向实例，并执行返回生成好的虚拟dom
    return this.$options.render.call(this);
  }

  // 1-2、根据虚拟dom 创建真实dom，更新视图
  Vue.prototype._update = function (vnode) {
    const vm = this;
    const el = vm.$el;
    // 保存一份组件第一次渲染的虚拟dom，即老的虚拟dom
    const preVnode = vm._vnode;
    vm._vnode = vnode;

    if (preVnode) {
      // 将保存的旧虚拟dom 和新的虚拟dom 进行diff 运算，差异更新
      vm.$el = patch(preVnode, vnode);
    } else {
      // 否则就是初始化渲染，依据生成的虚拟dom，创建真实dom，并更新
      vm.$el = patch(el, vnode);
    }
  }
}

// 2、挂载组件的钩子函数
export function mountComponent(vm, el) {
  vm.$el = el;

  // 调用render 方法，生成虚拟dom，创建对应的真实dom，并插入到el 元素节点中
  const updateComponent = () => vm._update(vm._render());

  // 对虚拟dom 进行观测，收集依赖，并更新视图
  new Watcher(vm, updateComponent, true);
}

// 3、调用生命周期钩子函数，钩子函数维护在队列里（组件本身的 > mixin 注入的）
export function callHook(vm, hook) {
  const handlers = vm.$options[hook];

  if (handlers) handlers.forEach(handler => handler.call(vm));
}